# protocol_handler.py
import struct


# =============================================================================
# 协议核心：Modbus CRC16 校验
# =============================================================================
def modbus_crc16(data: bytes) -> bytes:
    """计算Modbus RTU的CRC16校验码"""
    crc = 0xFFFF
    polynomial = 0xA001
    for byte in data:
        crc ^= byte
        for _ in range(8):
            if crc & 0x0001:
                crc = (crc >> 1) ^ polynomial
            else:
                crc >>= 1
    return struct.pack('<H', crc)


# =============================================================================
# 命令帧构建函数
# =============================================================================
def build_command_frame(payload: bytes, device_address: int) -> bytes:
    """
    根据负载和设备地址，构建一个完整的Modbus RTU命令帧。
    """
    modbus_data = b'g' + payload
    frame_without_crc = struct.pack('B', device_address) + modbus_data
    full_frame = frame_without_crc + modbus_crc16(frame_without_crc)
    return full_frame


# =============================================================================
# 响应帧解析函数 (完整版)
# =============================================================================
def parse_frame(frame: bytes) -> (str, dict):
    """
    解析一个完整的、经过CRC校验的响应帧。
    Returns:
        tuple: (log_string, parsed_data_dict)
    """
    app_payload = frame[3:-2]
    if not app_payload:
        return "警告: 收到空的应用层数据", {'type': 'empty'}

    main_cmd = chr(app_payload[0])
    log_details = ""
    parsed_data = {'type': 'unknown', 'raw_command': f"{main_cmd}"}

    try:
        # ---------------------------------------------------------------------
        # case (byte)'a': (系统状态与测量)
        # ---------------------------------------------------------------------
        if main_cmd == 'a':
            sub_cmd = chr(app_payload[1])
            parsed_data['raw_command'] += sub_cmd
            if sub_cmd == 'b' and chr(app_payload[2]) == 'f':
                vals = struct.unpack_from('<7f', app_payload, 3)
                parsed_data.update({'type': 'bus_measurement', 'data': vals})
                log_details = (f"  解析 [总线测量 'abf']:\n"
                               f"    原始值: [{vals[0]:.0f}, {vals[1]:.0f}], cur={vals[2]:.2f}A, vol={vals[3]:.3f}V\n"
                               f"    tick={vals[4]:.0f}, watchdog={vals[5]:.0f}, systick={vals[6]:.0f}")
            elif sub_cmd == 'z':
                en_val = app_payload[2]
                isopenloop_val = app_payload[3]
                mode_val = app_payload[4]
                autorun = app_payload[5]
                plimitEn = app_payload[6]
                fault, faultmask = struct.unpack_from('<2I', app_payload, 7)
                f = struct.unpack_from('<18f', app_payload, 15)
                parsed_data.update({
                    'type': 'system_status', 'en': en_val > 0, 'is_openloop': isopenloop_val > 0,
                    'mode': mode_val, 'autorun': autorun, 'plimit_en': plimitEn,
                    'fault': fault, 'fault_mask': faultmask, 'floats': f
                })
                log_details = (
                    f"  解析 [系统状态 'az']:\n"
                    f"  -------------------------------\n"
                    f"    系统: 输出{'on' if en_val > 0 else 'off'}, {'openLoop' if isopenloop_val > 0 else 'closedLoop'}, {'voltage' if mode_val > 0 else 'current'} mode, autorun={autorun}, plimitEn={plimitEn}\n"
                    f"    错误: fault=0x{fault:08X}, faultMask=0x{faultmask:08X}, faultData={f[0]:.6f}\n"
                    f"    控制: duty={f[1]:.2f}%, povc={f[2]:.2f}, vovc={f[3]:.2f}, iovc={f[4]:.2f}\n"
                    f"    设置: ref poSet={f[5]:.2f}, voSet={f[6]:.2f}, ioSet={f[7]:.2f}\n"
                    f"    监测: po={f[8]:.2f}W, vo={f[9]:.2f}V, io={f[10]:.2f}A, vin={f[11]:.2f}V\n"
                    f"    保护: ovp={f[15]:.1f}V, uvp={f[16]:.1f}V, oc={f[17]:.1f}A\n"
                    f"    时间: calc_time={f[12]:.1f}us, Tick={f[13]:.0f}, En_Tick={f[14]:.0f}\n"
                    f"  -------------------------------"
                )
        # ---------------------------------------------------------------------
        # case (byte)'c': (配置与控制)
        # ---------------------------------------------------------------------
        elif main_cmd == 'c':
            sub_cmd = chr(app_payload[1])
            parsed_data['raw_command'] += sub_cmd
            if sub_cmd == 'a' and chr(app_payload[2]) == 'z':
                voltages = struct.unpack_from('<24f', app_payload, 3)
                parsed_data.update({'type': 'pd_voltage', 'voltages': voltages})
                log_details = "  解析 [PD电压 'caz']:\n"
                for i in range(12):
                    log_details += f"    CH{i + 1:02d}: {voltages[i]:.3f}V | Cal: {voltages[i + 12]:.3f}V\n"
            elif sub_cmd == 'g' and chr(app_payload[2]) == 's':
                code = app_payload[3]
                parsed_data.update({'type': 'ack', 'ack_type': 'flash_save', 'success': code == 0})
                log_details = f"  解析 [Flash保存 'cgs']:\n    {'flash save done.' if code == 0 else f'flash save error, code : {0 - code}'}"
            # ... 此处可继续添加对'c'命令族其他子命令的完整解析 ...

        # ---------------------------------------------------------------------
        # case (byte)'u': (UID & IAP)
        # ---------------------------------------------------------------------
        elif main_cmd == 'u':
            sub_cmd = chr(app_payload[1])
            parsed_data['raw_command'] += sub_cmd
            if sub_cmd == 'u':
                # C#通过位移手动解析大端数据，这里我们直接用'>'指定大端
                uid0, uid1, uid2 = struct.unpack_from('>3I', app_payload, 2)
                parsed_data.update({'type': 'uid_response', 'uid': f"{uid0:08X}_{uid1:08X}_{uid2:08X}"})
                log_details = f"  解析 [UID读取 'uu']:\n    UID: {parsed_data['uid']}"
            elif sub_cmd == 'o':  # IAP
                iap_sub_cmd = chr(app_payload[2])
                parsed_data['raw_command'] += iap_sub_cmd
                flag = chr(app_payload[3])
                if iap_sub_cmd == 'v':
                    validity = "合法的" if flag == '0' else "非法的"
                    parsed_data.update({'type': 'iap_ack', 'ack_type': 'main_code_valid', 'valid': flag == '0'})
                    log_details = f"  解析 [主程序校验 'uov']:\n    主程序是{validity}"
                elif iap_sub_cmd == 'f':
                    parsed_data.update({'type': 'iap_ack', 'ack_type': 'iap_finish', 'success': flag == '0'})
                    if flag == '0':
                        log_details = "  解析 [IAP完成 'uof']:\n    程序更新成功"
                    else:
                        log_details = f"  解析 [IAP完成 'uof']:\n    IAP程序更新失败, code: {flag}"
            # ... 此处可继续添加对'u'命令族其他子命令的完整解析 ...

        # ---------------------------------------------------------------------
        # case (byte)'F': (Data Acquisition Config)
        # ---------------------------------------------------------------------
        elif main_cmd == 'F':
            sub_cmd = chr(app_payload[1])
            parsed_data['raw_command'] += sub_cmd
            if sub_cmd == 'a':
                response_type = chr(app_payload[2])
                parsed_data['raw_command'] += response_type
                if response_type == 'r':  # Read config response
                    p1, p2, p3, t_mode, t_src, autosend, period, t_len = struct.unpack_from('<6B2f', app_payload, 3)
                    freq = 1.0 / period if period != 0 else 0
                    parsed_data.update({
                        'type': 'acq_config', 'p1': p1, 'p2': p2, 'p3': p3,
                        't_mode': t_mode, 't_src': t_src, 'autosend': autosend > 0,
                        'freq': freq, 't_len': t_len
                    })
                    log_details = (f"  解析 [读取采集配置 'Far']:\n"
                                   f"    绘图源: {p1}, {p2}, {p3}\n"
                                   f"    采样频率: {freq:.2f} Hz\n"
                                   f"    触发:模式={t_mode}, 源={t_src}, 阈值={t_len}\n"
                                   f"    自动回传: {autosend > 0}")
                elif response_type == 's':  # Write config response
                    parsed_data.update({'type': 'ack', 'ack_type': 'write_acq_config'})
                    log_details = "  解析 [写入采集配置 'Fas']:\n    设备确认: 配置已写入"

        # ---------------------------------------------------------------------
        # case (byte)'f': (Plot data)
        # ---------------------------------------------------------------------
        elif main_cmd == 'f':
            index = app_payload[1]
            blen = app_payload[2]
            flen = blen // 4
            # 注意：C#代码中会根据UI选项判断数据是UInt32还是float
            # 这里我们假设它总是float，因为这是更通用的情况
            fdata = struct.unpack_from(f'<{flen}f', app_payload, 3)
            parsed_data.update({'type': 'plot_data', 'channel': index, 'data_points': flen, 'data': fdata})
            log_details = f"  解析 [绘图数据 'f']:\n    收到绘图通道 {index} 的数据, 共 {flen} 个点."

        else:
            log_details = f"  收到未完全解析的主命令: '{main_cmd}' (Hex: {ord(main_cmd):02X})"
            parsed_data['type'] = 'unhandled_command'

        return log_details.strip(), parsed_data

    except (struct.error, IndexError) as e:
        error_msg = f"解析数据时发生错误 (数据长度或格式可能不匹配): {e}\n  原始负载: {app_payload.hex(' ').upper()}"
        parsed_data['type'] = 'error'
        return error_msg, parsed_data